<?php
// +----------------------------------------------------------------------
// | KITEGO-Admin「开箱即用」「人人全栈」
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2024 https://www.kitego.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed KITEGO并不是自由软件，未经许可不能去掉KITEGO相关版权
// +----------------------------------------------------------------------
// | Author: KITEGO Team <bd@kitego.cn>
// +----------------------------------------------------------------------

namespace kitego\middleware;

class XssFilterMiddleware
{
    protected $exceptFields = [];

    /**
     * XSS防护中间件
     */
    public function handle($request, \Closure $next)
    {
        // 获取所有请求参数
        $params = $request->param();

        // 检查是否包含XSS风险
        if ($this->hasXssRisk($params)) {
            // 记录攻击尝试
            kg_file_put_contents('xss_attack', [
                'ip: ' . $request->ip(),
                'url:' . $request->url(),
                'params:' . json_encode($params)
            ]);

            return fail('请求包含不安全内容，已被系统拦截');
        }

        return $next($request);
    }

    /**
     * 检查参数是否包含XSS风险
     */
    private function hasXssRisk($params)
    {
        // XSS特征正则表达式
        $patterns = [
            '/<script.*?>.*?<\/script.*?>/is',        // <script>标签
            '/<.*?on\w+=".*?".*?>/i',                 // 事件处理属性
            '/<.*?style=".*?".*?>/i',                 // 样式注入
            '/<.*?href=".*?javascript:.*?".*?>/i',    // JavaScript URL
            '/<.*?src=".*?".*?>/i',                   // 图片/脚本源
            '/eval\((.*?)\)/i',                       // eval()函数
            '/expression\((.*?)\)/i',                 // CSS表达式
            '/vbscript:.*?/i',                        // VBScript
            '/base64,.*?/i',                          // Base64编码
            '/document\.cookie/i',                    // Cookie访问
            '/document\.location/i',                  // 页面重定向
            '/<\/?[^>]*>/i',                          // HTML标签
        ];

        // 遍历所有参数进行检查
        foreach ($params as $key => $value) {

            if (in_array($key, $this->exceptFields)) continue;

            // 如果值是数组，递归检查
            if (is_array($value)) {
                if ($this->hasXssRisk($value)) {
                    return true;
                }
            } else {
                // 检查字符串值
                foreach ($patterns as $pattern) {
                    if (!is_null($value) && preg_match($pattern, $value)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }
}